package top.codedance.iotp.server.net.handle;

import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.common.constent.ClientType;
import top.codedance.iotp.common.constent.Command;
import top.codedance.iotp.common.constent.MailContentTemplate;
import top.codedance.iotp.common.entity.CommonEntity;
import top.codedance.iotp.common.entity.DeviceEntity;
import top.codedance.iotp.common.handler.AbsractIdleHandler;
import top.codedance.iotp.common.util.DeviceIdUtil;
import top.codedance.iotp.common.util.HttpRequestUtil;
import top.codedance.iotp.common.util.MacUtils;
import top.codedance.iotp.server.ServerApplication;
import top.codedance.iotp.server.net.TcpServer;
import top.codedance.iotp.server.net.WsServer;
import top.codedance.iotp.server.net.handle.configuration.ClientHandlerConfig;
import top.codedance.iotp.server.net.handle.entity.ReceiverQueueEntity;
import top.codedance.iotp.server.net.handle.otherHandle.DownloadHandle;
import top.codedance.iotp.server.net.handle.otherHandle.GetLoadHandle;
import top.codedance.iotp.server.net.handle.otherHandle.ReportLoadHandle;
import top.codedance.iotp.server.net.handle.util.DeviceService;
import top.codedance.iotp.server.net.session.Session;
import cd.io.netty.channel.ChannelFutureListener;
import cd.io.netty.channel.ChannelHandlerContext;
import org.apache.log4j.Logger;

import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

public class IdleChannelHandler extends AbsractIdleHandler {

    private static Logger logger = ServerApplication.getLogger(IdleChannelHandler.class);

    public IdleChannelHandler(Logger logger) {
        super(logger);
    }

    @Override
    protected void messageReceived(ChannelHandlerContext channelHandlerContext, CommonEntity commonEntity) throws Exception {
        if (ServerApplication.isSafyMode && !ClientHandlerConfig.consoleCmds.contains(commonEntity.getCommand()) && !ClientHandlerConfig.healthyCmds.contains(commonEntity.getCommand())) {
            channelHandlerContext.close();
            return;
        }
        //注册
        if (commonEntity.getCommand() == Command.REGISTER) {
            CommonEntity commonEntity2 = new CommonEntity(ServerApplication.getDataVersion());
            commonEntity2.setCommand(Command.REGISTER_RECEIVE);
            String bodyJson = new String(commonEntity.getBody(), StandardCharsets.UTF_8);
            JSONObject body = JSONObject.parseObject(bodyJson);
            String deviceId = body.getString("deviceId");
            int clientType = body.getInteger("type");
            JSONObject jsonObject = new JSONObject();
            if (deviceId != null) {
                if(ServerApplication.blacklist.contains(deviceId)){
                    channelHandlerContext.close();
                    return;
                }
                String clientPublicKey = body.getString("publicKey");
                logger.debug("deviceId: " + deviceId);
                DeviceEntity de = new DeviceEntity();
                de.setDeviceId(deviceId);
                if (ServerApplication.isExits(de) || (clientType == ClientType.CONSOLE.getId() && channelHandlerContext.channel().remoteAddress().toString().indexOf("127.0.0.1") != -1)) {
                    if ((clientType == ClientType.CLIENT.getId() && !TcpServer.sessionManager.isOnline(deviceId))
                            || (clientType == ClientType.CLIENT_MANAGE.getId() && !TcpServer.sessionManager.isOnline(DeviceIdUtil.getClientManageDeviceId(deviceId)))
                            || (clientType == ClientType.CONSOLE.getId() && channelHandlerContext.channel().remoteAddress().toString().indexOf("127.0.0.1") != -1)
                    ) {
                        if (clientType == ClientType.CONSOLE.getId() && channelHandlerContext.channel().remoteAddress().toString().indexOf("127.0.0.1") != -1 && !ServerApplication.isExits(de)) {
                            ServerApplication.consoleCounter.getAndIncrement();
                            deviceId = DeviceIdUtil.genConsoleDeviceId(deviceId, ServerApplication.consoleCounter.get());
                        }
                        if(clientType == ClientType.CLIENT_MANAGE.getId()){
                            deviceId = DeviceIdUtil.getClientManageDeviceId(deviceId);
                        }
                        Session session = TcpServer.sessionManager.registry(channelHandlerContext.channel(), deviceId, clientPublicKey);
                        String serverPublicKey = session.getKeyStringPair().getPublicKey();
                        //注册到本地客户端状态队列
                        JSONObject receiverJSONObject = new JSONObject();
                        receiverJSONObject.put("deviceId", deviceId);
                        receiverJSONObject.put("status", "online");
                        ReceiverQueueEntity receiverQueueEntity = new ReceiverQueueEntity(Command.ASYNC_CSTATUS_COMMAND_RECEIVE, receiverJSONObject);
                        WsServer.localTaskReceiverQueue.offer(receiverQueueEntity); //通知到本地回复对列
                        //分布式注册
                        if (ServerApplication.enableDistributed && clientType == ClientType.CLIENT.getId()) {
                            JSONObject registryJSONObject = new JSONObject();
                            registryJSONObject.put("deviceId", deviceId);
                            if(ServerApplication.bindIp.equals("0.0.0.0")){
                                List<String> hostIps = MacUtils.getHostIp();
                                if(hostIps.size() > 0){
                                    registryJSONObject.put("server", hostIps.get(0) + ":" + ServerApplication.tcpPort);
                                }
                            }else{
                                registryJSONObject.put("server", ServerApplication.bindIp + ":" + ServerApplication.tcpPort);
                            }
                            final String log = "regist deviceid[" + deviceId + "] to register center.";
                            ServerApplication.loadBlanceRegists((registryHost) -> {
                                JSONObject post = HttpRequestUtil.post(registryHost + "/rest/api/server/register", jsonObject.toJSONString());
                                if (post.getIntValue("status") != 200) {
                                    return null;
                                }else{
                                    logger.debug(log);
                                    return "success";
                                }
                            });
                        }
                        jsonObject.put("msg", "Registry success");
                        jsonObject.put("serverPublicKey", serverPublicKey);
                        commonEntity2.setBody(jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8));
                        channelHandlerContext.writeAndFlush(commonEntity2.build());
                        if (ServerApplication.enabalMail && deviceId != null) {
                            DeviceEntity device;
                            if(ServerApplication.userVerifitionPrivateKey != null) {
                                device = DeviceService.getDevice(deviceId, ServerApplication.userVerifitionPrivateKey);
                            }else{
                                device = DeviceService.getDevice(deviceId);
                            }
                            if (device != null) {
                                ServerApplication.mailUtil.send(MailContentTemplate.ON_OFF_LINE_TIPS_SUBJECT, String.format(MailContentTemplate.ON_OFF_LINE_CONTENT, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), device.getUnitName(), device.getDeviceId(), "OnLine"));
                            }
                        }
                        logger.info("---device " + deviceId + " registry---");
                    } else {
                        jsonObject.put("msg", "Registry fail, access denied.");
                        commonEntity2.setBody(jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8));
                        channelHandlerContext.writeAndFlush(commonEntity2.build()).addListener(ChannelFutureListener.CLOSE);
                        logger.info("---device " + deviceId + " registried, access denied---");
                    }
                } else {
                    jsonObject.put("msg", "Registry fail, unauthorized.");
                    commonEntity2.setBody(jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8));
                    channelHandlerContext.writeAndFlush(commonEntity2.build()).addListener(ChannelFutureListener.CLOSE);
                    logger.info("---device " + deviceId + " unauthorized, access denied---");
                }
            } else {
                jsonObject.put("msg", "Registry fail, parameter fail.");
                commonEntity2.setBody(jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8));
                channelHandlerContext.writeAndFlush(commonEntity2.build()).addListener(ChannelFutureListener.CLOSE);
                logger.info("---device " + deviceId + " fail---");
            }
        }
        //接收客户端升级查询请求
        if (commonEntity.getCommand() == Command.UPGRADE) {
            String currentVersion = new String(commonEntity.getBody());
            int currentVersionCode = Integer.parseInt(currentVersion);
            if ((ServerApplication.compatible && currentVersionCode < ServerApplication.getLastClientVersionCode()) ||
                    (!ServerApplication.compatible && currentVersionCode != ServerApplication.getLastClientVersionCode())) {
                CommonEntity commonEntity3 = new CommonEntity();
                commonEntity3.setCommand(Command.UPGRADE_RECEIVE);
                commonEntity3.setBody(String.valueOf(ServerApplication.getLastClientVersionCode()).getBytes(StandardCharsets.UTF_8));
                channelHandlerContext.writeAndFlush(commonEntity3.build());
                logger.info("send client do update.");
            }
        }
        //接收客户端下载请求
        if (commonEntity.getCommand() == Command.DOWNLOAD) {
            Thread t = new Thread(new DownloadHandle(channelHandlerContext, commonEntity));
            t.start();
        }
        //上报负载信息请求
        if (commonEntity.getCommand() == Command.REPORT_LOAD) {
            Thread t = new Thread(new ReportLoadHandle(channelHandlerContext, commonEntity));
            t.start();
        }
        //获取负载信息请求
        if (commonEntity.getCommand() == Command.GET_LOAD) {
            Thread t = new Thread(new GetLoadHandle(channelHandlerContext, commonEntity));
            t.start();
        }
        super.messageReceived(channelHandlerContext, commonEntity);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception {
        String deviceId = null;
        Session session = TcpServer.sessionManager.getSessionById(channelHandlerContext.channel().id().toString());
        if (session != null) {
            deviceId = session.getDeviceId();
        }
        logger.info("---client " + channelHandlerContext.channel().remoteAddress().toString() + "|" + deviceId + " diconnected---");
        channelHandlerContext.close();
    }

    @Override
    protected void handleReaderIdle(ChannelHandlerContext channelHandlerContext) {
        logger.info("---client " + channelHandlerContext.channel().remoteAddress().toString() + " reader timeout, close it---");
        channelHandlerContext.close();
        super.handleReaderIdle(channelHandlerContext);
    }

    public void handlerAdded(ChannelHandlerContext channelHandlerContext) throws Exception {
        super.handlerAdded(channelHandlerContext);
    }

    public void handlerRemoved(ChannelHandlerContext channelHandlerContext) throws Exception {
        super.handlerRemoved(channelHandlerContext);
    }

    public void channelRegistered(ChannelHandlerContext channelHandlerContext) throws Exception {
        final ChannelHandlerContext ctx = channelHandlerContext;
        ServerApplication.syslogInfo("---client " + channelHandlerContext.channel().remoteAddress().toString() + " registered---");
        TcpServer.sessionManager.onLine(channelHandlerContext.channel(), channelHandlerContext.channel().id().toString());
        super.channelRegistered(channelHandlerContext);
    }

    public void channelUnregistered(ChannelHandlerContext channelHandlerContext) throws Exception {
        ServerApplication.syslogInfo("---client " + channelHandlerContext.channel().remoteAddress().toString() + " unregistered---");
        Session session = TcpServer.sessionManager.getSessionById(channelHandlerContext.channel().id().toString());
        String deviceId = session.getDeviceId();
        TcpServer.sessionManager.offLine(channelHandlerContext.channel(), channelHandlerContext.channel().id().toString());
        //取消注册到本地客户端状态队列
        JSONObject receiverJSONObject = new JSONObject();
        receiverJSONObject.put("deviceId", deviceId);
        receiverJSONObject.put("status", "offline");
        ReceiverQueueEntity receiverQueueEntity = new ReceiverQueueEntity(Command.ASYNC_CSTATUS_COMMAND_RECEIVE, receiverJSONObject);
        WsServer.localTaskReceiverQueue.offer(receiverQueueEntity); //通知到本地回复对列

        if (ServerApplication.enableDistributed && deviceId != null && DeviceIdUtil.checkIsClient(deviceId)) {
            ServerApplication.loadBlanceRegists((registryHost) -> {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("deviceId", deviceId);
                JSONObject post = HttpRequestUtil.post(registryHost + "/rest/api/client/unregister", jsonObject.toJSONString());
                if (post.getIntValue("status") != 200) {
                    return null;
                }else{
                    logger.debug("Unregister client [" + deviceId + "]");
                    return "success";
                }
            });
        }
        if (ServerApplication.enabalMail && deviceId != null) {
            DeviceEntity device;
            if(ServerApplication.userVerifitionPrivateKey != null) {
                device = DeviceService.getDevice(deviceId, ServerApplication.userVerifitionPrivateKey);
            }else{
                device = DeviceService.getDevice(deviceId);
            }
            if (device != null) {
                ServerApplication.mailUtil.send(MailContentTemplate.ON_OFF_LINE_TIPS_SUBJECT, String.format(MailContentTemplate.ON_OFF_LINE_CONTENT, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), device.getUnitName(), device.getDeviceId(), "OffLine"));
            }
        }
        channelHandlerContext.close();
    }

    public void channelActive(ChannelHandlerContext channelHandlerContext) throws Exception {
        super.channelActive(channelHandlerContext);
    }

    public void channelInactive(ChannelHandlerContext channelHandlerContext) throws Exception {
        super.channelInactive(channelHandlerContext);
    }

    public void channelRead(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {
        super.channelRead(channelHandlerContext, msg);
    }

    public void channelReadComplete(ChannelHandlerContext channelHandlerContext) throws Exception {
        super.channelReadComplete(channelHandlerContext);
    }

    public void userEventTriggered(ChannelHandlerContext channelHandlerContext, Object evt) throws Exception {
        super.userEventTriggered(channelHandlerContext, evt);
    }

    public void channelWritabilityChanged(ChannelHandlerContext channelHandlerContext) throws Exception {
        super.channelWritabilityChanged(channelHandlerContext);
    }
}
